﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using NyARToolkitCSUtils.Direct3d;

namespace Client
{
    [StructLayout(LayoutKind.Sequential)]
    struct Vertex
    {
        public Vector4 PositionRhw;
        public int Color;
    }
	/// <summary>
	/// Interaction logic for Canvas3D.xaml
	/// </summary>
	public partial class Canvas3D
	{

        private D3DImage mD3DImg;
        private Device mDevice;
        private IntPtr mScene;
        private VertexBuffer mBuffer = null;
        private IndexBuffer mIBuffer = null;
        Microsoft.DirectX.Matrix mPrev = new Microsoft.DirectX.Matrix();

        /*private void RenderScene(ArrayList _matrix)
        {
            if (_matrix.Count <= 0)
                return;
            
            Microsoft.DirectX.Matrix dxMat = (Microsoft.DirectX.Matrix)_matrix[0];

            

            mDevice.SetStreamSource(0, mBuffer, 0);

            // 描画する頂点のフォーマットをセット
            mDevice.VertexFormat = CustomVertex.PositionColored.Format;

            // インデックスバッファをセット
            mDevice.Indices = mIBuffer;

            //立方体を20mm上（マーカーの上）にずらしておく
            Microsoft.DirectX.Matrix transform_mat2 = Microsoft.DirectX.Matrix.Translation(0, 0, 20.0f);
            transform_mat2 *= dxMat;
            mDevice.SetTransform(TransformType.World, transform_mat2);

            // レンダリング（描画）
            mDevice.RenderState.CullMode = Cull.Clockwise;
            mDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 8, 0, 12);
        }

        private void ReleaseScene()
        {
            if (mDevice == null)
                return;

            mDevice.Dispose();
            mDevice = null;

            mD3DImg.Lock();
            mD3DImg.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
            mD3DImg.Unlock();
        }
        */
		public Canvas3D()
		{
			this.InitializeComponent();
		}
/*
        private static Int16[] _vertexIndices = new Int16[] { 2, 0, 1, 1, 3, 2, 4, 0, 2, 2, 6, 4, 5, 1, 0, 0, 4, 5, 7, 3, 1, 1, 5, 7, 6, 2, 3, 3, 7, 6, 4, 6, 7, 7, 5, 4 };

        public void init(int _width, int _height)
        {
            this.Width = _width;
            this.Height = _height;


            mD3DImg = new D3DImage();
            //mD3DImg.IsFrontBufferAvailableChanged += new DependencyPropertyChangedEventHandler(mD3D_IsFrontBufferAvailableChanged);
            ImageBrush img = new ImageBrush(mD3DImg);
            LayoutRoot.Background = img;


            BeginRenderingScene();

            mBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored),
                8, mDevice, Usage.None, CustomVertex.PositionColored.Format, Pool.Managed);
            

            CustomVertex.PositionColored[] vertices = new CustomVertex.PositionColored[8];
            const float CUBE_SIZE = 20.0f;//1辺40[mm]の
            //頂点を設定
            vertices[0] = new CustomVertex.PositionColored(-CUBE_SIZE, CUBE_SIZE, CUBE_SIZE, System.Drawing.Color.Yellow.ToArgb());
            vertices[1] = new CustomVertex.PositionColored(CUBE_SIZE, CUBE_SIZE, CUBE_SIZE, System.Drawing.Color.Gray.ToArgb());
            vertices[2] = new CustomVertex.PositionColored(-CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE, System.Drawing.Color.Purple.ToArgb());
            vertices[3] = new CustomVertex.PositionColored(CUBE_SIZE, CUBE_SIZE, -CUBE_SIZE, System.Drawing.Color.Red.ToArgb());
            vertices[4] = new CustomVertex.PositionColored(-CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE, System.Drawing.Color.SkyBlue.ToArgb());
            vertices[5] = new CustomVertex.PositionColored(CUBE_SIZE, -CUBE_SIZE, CUBE_SIZE, System.Drawing.Color.Orange.ToArgb());
            vertices[6] = new CustomVertex.PositionColored(-CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE, System.Drawing.Color.Green.ToArgb());
            vertices[7] = new CustomVertex.PositionColored(CUBE_SIZE, -CUBE_SIZE, -CUBE_SIZE, System.Drawing.Color.Blue.ToArgb());

            using (GraphicsStream data = mBuffer.Lock(0, 0, LockFlags.None))
            {
                // 頂点データを頂点バッファにコピーします
                data.Write(vertices);

                // 頂点バッファのロックを解除します
                mBuffer.Unlock();
            }

            // インデックスバッファの作成
            // 第２引数の数値は(三角ポリゴンの数)*(ひとつの三角ポリゴンの頂点数)*
            // (16 ビットのインデックスサイズ(2byte))
            mIBuffer = new IndexBuffer(mDevice, 12 * 3 * 2, Usage.WriteOnly,
                Pool.Managed, true);

            // インデックスバッファをロックする
            using (GraphicsStream data = mIBuffer.Lock(0, 0, LockFlags.None))
            {
                // インデックスデータをインデックスバッファにコピーします
                data.Write(_vertexIndices);

                // インデックスバッファのロックを解除します
                mIBuffer.Unlock();
            }

            //Core.Instance.mSURFACE = new NyARSurface_XRGB32(mDevice, 640, 480);
        }

        public void updateScene(ArrayList _matrix)
        {
            mD3DImg.Dispatcher.Invoke((Action)(delegate
            {
                try
                {
                    if (mD3DImg.IsFrontBufferAvailable && mScene != IntPtr.Zero)
                    {
                        mD3DImg.Lock();
 
                        Surface dest_surface = mDevice.GetBackBuffer(0, 0, BackBufferType.Mono);
                        System.Drawing.Rectangle src_dest_rect = new System.Drawing.Rectangle(0, 0, 640, 480);
                        mDevice.StretchRectangle(Core.Instance.mSURFACE.d3d_surface, src_dest_rect, dest_surface, src_dest_rect, TextureFilter.None);


                        mDevice.BeginScene();
                        mDevice.Clear(ClearFlags.ZBuffer, System.Drawing.Color.Wheat, 1.0f, 0);
                        
                        RenderScene(_matrix);
                        mDevice.EndScene();
                        mDevice.Present();

                        mD3DImg.SetBackBuffer(D3DResourceType.IDirect3DSurface9, mScene);
                        mD3DImg.AddDirtyRect(new Int32Rect(0, 0, 640,480));
                        mD3DImg.Unlock();
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(ex.Message);
                }
            }));

           /* if (mPrev != null && _matrix.Count > 0)
            {
                System.Diagnostics.Debug.WriteLine("\n-----------------------");
                System.Diagnostics.Debug.WriteLine((mDevice.Transform.World - mPrev).ToString());
                mPrev = mDevice.Transform.World;
            }
            
            //System.Diagnostics.Debug.WriteLine("Projection:\n"+mDevice.GetTransform(TransformType.Projection).ToString());
            //System.Diagnostics.Debug.WriteLine("\nView:\n"+mDevice.GetTransform(TransformType.View).ToString());
            //System.Diagnostics.Debug.WriteLine("\nWorld:\n" + mDevice.GetTransform(TransformType.World).ToString());
            return;
        }

        void BeginRenderingScene()
        {
            ReleaseScene();

            HwndSource i_window = new HwndSource(0, 0, 0, 0, 0, "ARSS", IntPtr.Zero);

            PresentParameters pp = new PresentParameters();
            pp.Windowed = true;
            pp.SwapEffect = SwapEffect.Discard;
            pp.BackBufferFormat = Format.X8R8G8B8;
            pp.BackBufferCount = 1;
            pp.EnableAutoDepthStencil = true;
            pp.AutoDepthStencilFormat = DepthFormat.D16;
            pp.BackBufferHeight = 480;
            pp.BackBufferWidth = 640;
            CreateFlags fl_base = CreateFlags.FpuPreserve;
            try
            {
                mDevice = new Device(0, DeviceType.Hardware, i_window.Handle, fl_base | CreateFlags.HardwareVertexProcessing, pp);
            }
            catch (Exception ex1)
            {
                try
                {
                    mDevice = new Device(0, DeviceType.Hardware, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                }
                catch (Exception ex2)
                {
                    // 作成に失敗
                    try
                    {
                        mDevice = new Device(0, DeviceType.Reference, i_window.Handle, fl_base | CreateFlags.SoftwareVertexProcessing, pp);
                    }
                    catch (Exception ex3)
                    {
                        throw ex3;
                    }
                }
            }

            Surface s = mDevice.GetBackBuffer(0, 0, BackBufferType.Mono);
            unsafe
            {
                mScene = new IntPtr(s.UnmanagedComPointer);
            }
            

            mD3DImg.Lock();
            mD3DImg.SetBackBuffer(D3DResourceType.IDirect3DSurface9, mScene);
            mD3DImg.Unlock();

            //CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            
        }

        void StopRenderingScene()
        {
            //CompositionTarget.Rendering -= CompositionTarget_Rendering;
            ReleaseScene();
            mScene = IntPtr.Zero;
        }

        void mD3D_IsFrontBufferAvailableChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (mD3DImg.IsFrontBufferAvailable)
                BeginRenderingScene();
            else
                StopRenderingScene();

        }

        public void alignCamera(float[] _matrix)
        {

            mDevice.Transform.Projection = Core.ARWPFUtil.getDXMatrix(_matrix);
            mDevice.Transform.View =Microsoft.DirectX.Matrix.LookAtLH(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 0.0f, 1.0f), new Vector3(0.0f, 1.0f, 0.0f));


            Viewport vp = new Viewport();
            vp.X = 0;
            vp.Y = 0;
            vp.Height = (int)this.Height;
            vp.Width = (int)this.Width;
            vp.MaxZ = 1.0f;
            //ビューポート設定
            mDevice.Viewport = vp;

        }*/
	}
}